import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.Arrays;
import joe.AudioMini;
import joe.Engine;
import joe.Font;
import joe.InputGlfw;
import joe.Render;
import joe.RenderGles3;
import joe.UnsafeUtil;
import org.lwjgl.system.MemoryUtil;

// -Dstdout.encoding=utf-8 -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI --enable-native-access=ALL-UNNAMED --add-opens java.base/jdk.internal.misc=ALL-UNNAMED
public class GenFontTex {
	public static int[] genAllAscII() {
		var cs = new int[0x5f];
		for (int i = 0x20; i <= 0x7e; i++)
			cs[i - 0x20] = i;
		return cs;
	}

	// GB2312和GBK不同的地方:
	// 全角中心小圆点: Unicode有0x00b7(GBK)和0x30fb(GB2312), GB2312有0xa1a4
	// 全角中心长横线: Unicode有0x2014(长破折号,GBK),0x2015(短破折号,GB2312)和0x2500(制表横线), GB2312有0xa1aa(破折号)和0xa9a4(制表横线)
	public static int[] genAllGB2312() {
		var dec = Charset.forName("GB2312").newDecoder();
		var bb = ByteBuffer.allocate(2);
		var cb = CharBuffer.allocate(1);
		var buf = new int[0x8000];
		int n = 0;
		for (int i = 0xa1; i <= 0xf7; i++) {
			if (i >= 0xaa && i <= 0xaf)
				continue;
			bb.put(0, (byte)i);
			for (int j = 0xa1; j <= 0xfe; j++) {
				bb.put(1, (byte)j);
				bb.position(0);
				cb.position(0);
				var cr = dec.decode(bb, cb, true);
				if (!cr.isError() && cb.position() == 1) //TODO: maybe position==2
					buf[n++] = cb.get(0);
			}
		}
		return Arrays.copyOf(buf, n);
	}

	static void main(@SuppressWarnings("unused") String[] args) {
//		var dec = Charset.forName("GB2312").newDecoder();
//		var cb = CharBuffer.allocate(1);
//		var cr = dec.decode(ByteBuffer.wrap(new byte[]{(byte)0xa1, (byte)0xff}), cb, true);
//
		var engine = Engine.current();
		try {
			engine.init(new RenderGles3(), new Render.InitParams(), new InputGlfw(), new AudioMini());
			engine.getResourceManager().recollectAll("res");
			var font = engine.getResourceManager().addFont("default", "LXGWWenKaiGB-Regular.ttf", 50, 4096);
			System.out.println("baseline: " + font.getBaseline());

			var buf = new int[0x10000];
			var flags = new boolean[0x10000];
			int n = 0;
			var cs = genAllAscII();
			for (var c : cs) {
				if (!flags[c] && font.hasCodePoint(c)) {
					flags[c] = true;
					buf[n++] = c;
				}
			}
			cs = genAllGB2312();
			for (var c : cs) {
				if (!flags[c] && font.hasCodePoint(c)) {
					flags[c] = true;
					buf[n++] = c;
				}
			}
			cs = new int[]{ // 补充
					0x00b7, 0x30fb, // '·','・'
					0x2014, 0x2015, 0x2500, // '—', '―', '─'
					0x00a9, 0x00ae, 0x2122, // '©', '®', '™'
			};
			for (var c : cs) {
				if (!flags[c] && font.hasCodePoint(c)) {
					flags[c] = true;
					buf[n++] = c;
				}
			}
			System.out.println("charCount: " + n);
			for (int i = 0; i < n; i++) {
				var c = buf[i];
				var info = font.getOrAddCharInfo(c);
				System.out.format("tx/y=%4d/%4d tw/h=%2d/%2d ox/y=%2d/%2d adv=%2d c=%5d(%c)\n",
						Font.toTexX(info), Font.toTexY(info), Font.toTexW(info), Font.toTexH(info),
						Font.toOffsetX(info), Font.toOffsetY(info), Font.toAdvanceX(info), c, (char)c);
			}

			var tex = font.getTexture();
			var texW = tex.width();
			var texH = tex.height();
			var dataSize = (long)texW * texH;
			var render = engine.getRender();
			var frame = render.createFrame();
			frame.bindTexture(0, tex);
			var pbo = render.createDownloadPixelBuffer(dataSize); // 1ms
			render.readFrame(frame, 0, 1, 0, 0, texW, texH, pbo); // 0ms
			pbo.download(0, dataSize, dataPtr -> { // 1ms
				try (var fos = new FileOutputStream("~$font.tga")) { // 6ms
					final var head = new byte[18];
					head[2] = 3; // 2:24/32-bit; 3:8-bit
					head[12] = (byte)texW;
					head[13] = (byte)(texW >> 8);
					head[14] = (byte)texH;
					head[15] = (byte)(texH >> 8);
					head[16] = 8; // bits
					head[17] = 0x20; // 0:bottom=>top; 0x20:from top=>bottom
					fos.write(head);
					if (fos.getChannel().write(MemoryUtil.memByteBuffer(dataPtr, (int)dataSize)) != dataSize)
						throw new AssertionError();
				} catch (Exception e) {
					UnsafeUtil.forceThrow(e);
				}
			});
		} finally {
			engine.destroy();
		}
		System.out.println("end");
	}
}
